package com.train.common.aop.interceptor;

import cn.hutool.http.Header;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;

/**
 * 使用Aop拦截Controller层需要进行日志记录的API
 *
 * @author cxg
 */
@Slf4j
@Aspect
@Order
public class ApiLogAop {

    /**
     * 记录module下所有rest子包的controller
     */
    @Pointcut("execution(public * com.train.mgr.modules.*.controller.*.*(..))"
            + "||execution(public * com.train.*.modules.*.*(..))")
    public void webLog() {
    }

    /**
     * 在方法执行前执行
     *
     * @param joinPoint 连接点
     */
    @Before(value = "webLog()")
    private void doBefore(JoinPoint joinPoint) {
        //判空，如果为空则返回
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        log.info("CLASS_METHOD : {}.{}",
                joinPoint.getSignature().getDeclaringTypeName(),
                joinPoint.getSignature().getName());

        String contentType = request.getContentType();
        if (contentType != null && contentType.contains(MimeTypeUtils.APPLICATION_JSON_VALUE)) {
            Object[] args = joinPoint.getArgs();
            // 过滤掉不适合序列化的参数
            Object[] filteredArgs = Arrays.stream(args)
                    .filter(arg -> !(arg instanceof HttpServletResponse))
                    .filter(arg -> !(arg instanceof MultipartFile))
                    .filter(arg -> !(arg instanceof InputStream))
                    .filter(arg -> !(arg instanceof OutputStream))
                    .toArray();

            if (filteredArgs.length > 0) {
                try {
                    log.info("args : {}", JSONObject.toJSONString(filteredArgs[0]));
                } catch (Exception e) {
                    log.warn("Failed to serialize args to JSON: {}", e.getMessage());
                    log.info("args : {}", Arrays.toString(filteredArgs));
                }
            }
        } else {
            // 同样过滤不适合输出的参数
            Object[] filteredArgs = Arrays.stream(joinPoint.getArgs())
                    .filter(arg -> !(arg instanceof HttpServletResponse))
                    .filter(arg -> !(arg instanceof MultipartFile))
                    .filter(arg -> !(arg instanceof InputStream))
                    .filter(arg -> !(arg instanceof OutputStream))
                    .toArray();
            log.info("args : {}", Arrays.toString(filteredArgs));
        }
    }

    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) throws Throwable {
        try {
            // 避免序列化ServletResponse等对象
            if (ret != null &&
                    !(ret instanceof HttpServletResponse) &&
                    !(ret instanceof ServletResponse) &&
                    !(ret instanceof InputStream) &&
                    !(ret instanceof OutputStream)) {
               // log.info("resp: " + JSONObject.toJSONString(ret));
            } else {
                //log.info("resp: [stream or response object]");
            }
        } catch (Exception e) {
            log.warn("Failed to serialize response to JSON: {}", e.getMessage());
            //log.info("resp: {}", ret != null ? ret.toString() : "null");
        }
        log.info("========================= Request =========================");
    }

}
